package templatemethod

import "fmt"

// AbstractTemplate 一个较为完整的模板定义示例
type AbstractTemplate struct {
	Implement
}

// Implement 算法中的步骤，就要具体实现
type Implement interface {
	operation1()
	operation2()
	// 算法中的必要步骤，需要实现
	doPrimitiveOperation1()
	doPrimitiveOperation2()
	hookOperation1()
}

func (t *AbstractTemplate) TemplateMethod() {
	// 第一步
	t.Implement.operation1()
	// 第二步
	t.Implement.operation2()
	// 第三部
	t.Implement.doPrimitiveOperation1()
	// 第四部
	t.Implement.doPrimitiveOperation2()
	// 第五步
	t.Implement.hookOperation1()
}

// operation1 算法中的步骤，固定实现
func (t *AbstractTemplate) operation1() {
	fmt.Println("AbstractTemplate operation1")
}

// operation2 算法中的步骤，固定实现
func (t *AbstractTemplate) operation2() {
	fmt.Println("AbstractTemplate operation2")
}

// hookOperation1 钩子操作，算法中的步骤，不一定需要，提供默认实现
func (t *AbstractTemplate) hookOperation1() {
	fmt.Println("AbstractTemplate hookOperation1")
}

type ConcreteImplA struct {
	*AbstractTemplate
}

// 因为Golang不提供继承机制，需要使用匿名组合模拟实现继承。
// 此处需要注意：因为父类需要调用子类方法，所以子类需要匿名组合父类的同时，父类需要持有子类的引用。

func NewConConcreteImplA() *ConcreteImplA {
	temp := &ConcreteImplA{}
	absTemp := &AbstractTemplate{
		Implement: temp,
	}
	temp.AbstractTemplate = absTemp
	return temp
}

func (a *ConcreteImplA) doPrimitiveOperation1() {
	fmt.Println("ConcreteImplA doPrimitiveOperation1")
}

func (a *ConcreteImplA) doPrimitiveOperation2() {
	fmt.Println("ConcreteImplA doPrimitiveOperation2")
}

func (a *ConcreteImplA) operation2() {
	fmt.Println("ConcreteImplA operation2")
}

type ConcreteImplB struct {
	*AbstractTemplate
}

// absTemp := &AbstractTemplate{}
func NewConConcreteImplB() *ConcreteImplB {
	temp := &ConcreteImplB{}
	absTemp := &AbstractTemplate{
		Implement: temp,
	}
	temp.AbstractTemplate = absTemp
	return temp
}

func (a *ConcreteImplB) doPrimitiveOperation1() {
	fmt.Println("ConcreteImplB doPrimitiveOperation1")
}

func (a *ConcreteImplB) doPrimitiveOperation2() {
	fmt.Println("ConcreteImplB doPrimitiveOperation2")
}

func (a *ConcreteImplB) hookOperation1() {
	fmt.Println("ConcreteImplB hookOperation1")
}
